/*
 * Created on 14.11.2012
 */
package net.tumblr.backup;

import java.sql.Timestamp;
import java.util.Date;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Represents a Tumblr post, with some getters and setters of easier property access.
 *
 * @author cgaffga
 * @version $Id: Post.java 10 2012-12-11 12:51:10Z cgaffga $
 */
public class Post extends JSONObject {

    /**
     * Returns a date from a JSON object, or {@code null} if the value is non-existent or not a valid number. A date
     * format like <i>YYYY-MM-DD hh:mm:ss...</i> is expected, we use the first 19 characters..
     *
     * @param json The JSON object.
     * @param key The key to lookup.
     * @return The value or {@code null}.
     */
    protected static Date getDate(final JSONObject json, final String key) {
        try {
            final Date value = new Date(Timestamp.valueOf(json.getString("date").substring(0, 19)).getTime());
            return "".equals(value) ? null : value;
        } catch (final JSONException | IllegalArgumentException | IndexOutOfBoundsException e) {
            return null;
        }
    }

    /**
     * Returns a number from a JSON object, or {@code null} if the value is non-existent or not a valid number.
     *
     * @param json The JSON object.
     * @param key The key to lookup.
     * @return The value or {@code null}.
     */
    protected static Long getLong(final JSONObject json, final String key) {
        try {
            final Long value = json.getLong(key);
            return "".equals(value) ? null : value;
        } catch (final JSONException e) {
            return null;
        }
    }

    /**
     * Returns a string from a JSON object, or {@code null} if the value is non-existent or empty.
     *
     * @param json The JSON object.
     * @param key The key to lookup.
     * @return The value or {@code null}.
     */
    protected static String getString(final JSONObject json, final String key) {
        try {
            final String value = json.getString(key);
            return "".equals(value) ? null : value;
        } catch (final JSONException e) {
            return null;
        }
    }

    /**
     * Default constructor.
     */
    public Post() {
        // nothing to initialize!
    }

    /**
     * Constructs a post from another JSON object, one that holds a post.
     *
     * @param json A JSON object containing a post, will be copied.
     */
    public Post(final JSONObject json) {
        super(json, JSONObject.getNames(json));
    }

    /**
     * Adds a photo's filename to the list <i>downloaded_photos</i>, it's the name of the backed up photo on disk.
     *
     * @param filename The name of the photo-file on disk, to be added to the list.
     * @throws IllegalArgumentException Thrown if a {@link JSONException} was thrown, should never happen!
     */
    public void addDownloadedPhoto(final String filename) {
        try {
            append("bu_photos", filename);
        } catch (final JSONException e) {
            throw new IllegalArgumentException("downloaded_photo_count should always be incrementable!", e);
        }
    }

    /**
     * Returns the blog name.
     *
     * @return Blog name <i>blog_name</i> or empty string.
     */
    public String getBlogName() {
        return optString("blog_name");
    }

    /**
     * Returns <i>downloaded_photos</i>, it's the names of the backed up photos on disk.
     *
     * @return List of downloaded photos or an empty array if non present.
     */
    public JSONArray getDownloadedPhotos() {
        final JSONArray downloadedPhotos = optJSONArray("bu_photos");
        return downloadedPhotos == null ? new JSONArray() : downloadedPhotos;
    }

    /**
     * Returns a JSON object with only the minimum of data needed to produce HTML files from the post.
     *
     * @return New JSON object with only id, date, link and photos.
     * @throws IllegalArgumentException Thrown if some JSON error occurs, should never happen!
     */
    public JSONObject getMinimum() throws IllegalArgumentException {
        try {
            final JSONObject min = new JSONObject();
            min.put("id", getPostId());
            min.put("date", getPostDate());
            min.put("url", getPostUrl());
            min.put("bu_photos", getDownloadedPhotos());
            return min;
        } catch (final JSONException e) {
            throw new IllegalArgumentException("JSON error that should not happen!", e);
        }
    }

    /**
     * Returns the <i>photos</i> of this post.
     *
     * @return Returns an array of photos or an empty collection if no photos present or it's not an array.
     */
    public JSONArray getPhotos() {
        final JSONArray photos = optJSONArray("photos");
        return photos == null ? new JSONArray() : photos;
    }

    /**
     * Returns the post's date.
     *
     * @return Post <i>date</i> or {@code null} if no date set or unparsable.
     */
    public Date getPostDate() {
        return getDate(this, "date");
    }

    /**
     * Returns the post's id.
     *
     * @return Returns the post's id.
     * @throws IllegalArgumentException Thrown if the id is not set or unparsable, cause by {@link JSONException}.
     */
    public long getPostId() throws IllegalArgumentException {
        try {
            return getLong("id");
        } catch (final JSONException e) {
            throw new IllegalArgumentException("Post id not parsable!", e);
        }
    }

    /**
     * Returns the post's URL.
     *
     * @return Returns the post's URL <i>post_url</i> or an empty string.
     */
    public String getPostUrl() {
        return optString("post_url");
    }

    /**
     * Returns the id of the reblogged post.
     *
     * @return Returns the <i>reblogged_from_id</i> or {@code null} if the field is not set.
     */
    public Long getRebloggedFromId() {
        return getLong(this, "reblogged_from_id");
    }

    /**
     * Returns the name of the reblogged blog.
     *
     * @return Returns the <i>reblogged_from_name</i> or {@code null} if the field is not set.
     */
    public String getRebloggedFromName() {
        return getString(this, "reblogged_from_name");
    }

    /**
     * Returns the URL of the reblogged post.
     *
     * @return Returns the <i>reblogged_from_url</i> or {@code null} if the field is not set.
     */
    public String getRebloggedFromUrl() {
        return getString(this, "reblogged_from_url");
    }

    /**
     * Returns the name of the root of the reblogged post.
     *
     * @return Returns the <i>reblogged_root_name</i> or {@code null} if the field is not set.
     */
    public String getRebloggedRootName() {
        return getString(this, "reblogged_root_name");
    }

    /**
     * Returns the URL of the root of the reblogged post.
     *
     * @return Returns the <i>reblogged_root_url</i> or {@code null} if the field is not set.
     */
    public String getRebloggedRootUrl() {
        return getString(this, "reblogged_root_url");
    }

    /**
     * Returns the reblog key.
     *
     * @return Returns the <i>reblog_key</i> or {@code null} if the field is not set.
     */
    public String getReblogKey() {
        return getString(this, "reblog_key");
    }

    /**
     * Returns the post's source title.
     *
     * @return Returns the <i>source_title</i> or {@code null} if the field is not set.
     */
    public String getSourceTitle() {
        return getString(this, "source_title");
    }

    /**
     * Returns the post's source URL.
     *
     * @return Returns the <i>source_url</i> or {@code null} if the field is not set.
     */
    public String getSourceUrl() {
        return getString(this, "source_url");
    }

}
